/****************************************************************************

	COPYRIGHT(C) MAXSI SOFTWARE, JONAS 'SORTIE' TERMANSEN 2008, 2009, 2010

    This file is part of Maxsi Distribution.

    Maxsi Distribution is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Maxsi Distribution is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with Maxsi Distribution. If not, see <http://www.gnu.org/licenses/>.

	*/ #include "MaxsiDistributionContributors.h" /*

	If you modify this file, please enter your name below and provide contact
	information in MaxsiDistributionContributors.h. For more information please see
	MaxsiDistributionContributors.h.
	
	Contributors to this file:

	- Jonas 'Sortie' Termansen
	- [your name here]

	MaxsiDistribution.dll
	Distributing since 2008

	MaxsiFileServer.cpp
	Async File Handling

****************************************************************************/

#include "MaxsiEngine.h"
#include "MaxsiDistributionLink.h"
#include "Thread.h"
#include "Data.h"
#include "MaxsiTorrent.h"
#include "MaxsiFileServer.h"


bool	ShouldDoSillyDebugStuffDeleteMe		=	false;

//=============================================================================
//	class ME_FileServerClient
//=============================================================================

ME_FileServerClient::ME_FileServerClient()
{
	m_Mode					=	FileServer_Inactive;
	CurrentFile				=	0;
	Socket					=	(SOCKET)INVALID_HANDLE_VALUE;
	CommandParam.Read		=	false;

	#ifdef _DEBUG
		LastThinkType		=	0;
	#endif

	// Todo: Remove:
	//if (!InitializeCriticalSectionAndSpinCount(&MayWriteToBlocks, 0x80000400) ||
	//	!InitializeCriticalSectionAndSpinCount(&MayExecThinkFunction, 0x80000400) )
	//{		
	//	ME_UNDEFINED_BEHAVIOR("ME_FileServerClient::ME_FileServerClient() couldn't initialize InitializeCriticalSectionAndSpinCount(&MayWriteToBlocks) && InitializeCriticalSectionAndSpinCount(&MayExecThinkFunction)!");
	//}
//#ifdef _DEBUG
//	nL_MayWriteToBlocks		=	0;
//#endif
	//BufferOffset			=	0;
	//IsWriting				=	0;
	//IsReading				=	0;
	//BufferUsed				=	0;
	//CurrentlyWriting		=	0;
	BufferOverflowDetected	=	false;
}

ME_FileServerClient::~ME_FileServerClient()
{
	if ( CurrentFile ) { delete[] CurrentFile; }
	if ( Socket != (SOCKET)INVALID_HANDLE_VALUE ) { CloseHandle((HANDLE)Socket); }
}

bool ME_FileServerClient::InitializeBuffers()
{
	// 1 MiB should be enough space for streaming parts of blocks without being choked too much.

	// The Disk Speed should be the limiting factor, though if this number is too low
	// this will be the limiting factor. Then please increase this. Make sure there is at
	// least room for a single block + overhead, if there isn't the MaxsiFileSever system
	// will deadlock.

	if ( Output ) { DeleteBuffer(Output); }	Output	=	CreateBuffer(1024*1024);

	// For decompression, we *need* at least two blocks.
	// One compressed and one uncompressed. 128 KiB shouldn't be too much
	// because the file system only has one client. We use a full buffer
	// because it should be faster than doing it part by part. (Is that
	// actually true, we don't consider harddisk latency?)

	if ( Input ) { DeleteBuffer(Input); }	Input	=	CreateBuffer(MaxsiTorrent_BlockSize*2);


	return true;
}

#ifdef _DEBUG

bool ME_FileServerClient::ScheduledNextThink(int NextThinkType, int Line, char* FileName)
{

	if ( NextThinkType != LastThinkType )
	{
		// Do debug stuff here.
		LastThinkType	=	NextThinkType;
	}

	// Used in debugging to point out scheduled thinks for only the ME_FileServerClient.
	return BaseClass::ScheduledNextThink(NextThinkType,Line,FileName);
}

#endif

// Replaced with ME_Buffer.

//
//size_t ME_FileServerClient::IN_BUFFER_SIZE()
//{
//	return MaxsiTorrent_BlockSize*2;
//}
//
//size_t ME_FileServerClient::IN_BUFFER_INC()
//{
//	return 2048;
//}
//
//
//size_t ME_FileServerClient::OUT_BUF_SIZE()
//{
//
//	return 1024*1024; 
//}
//
//
//void	ME_FileServerClient::Enter()
//{
//	int	lol = 101;
//	EnterCriticalSection(&MayWriteToBlocks);
//	int leet = 1337;
//#ifdef _DEBUG
//	nL_MayWriteToBlocks++;
//#endif
//}
//
//void	ME_FileServerClient::Leave()
//{
//#ifdef _DEBUG
//	nL_MayWriteToBlocks--;
//#endif
//	LeaveCriticalSection(&MayWriteToBlocks);
//}
//
//size_t	ME_FileServerClient::GetBufferOffset()
//{
//	return	BufferOffset;
//}
//
//size_t	ME_FileServerClient::GetBufferUsed()
//{
//	return	BufferUsed+CurrentlyWriting;
//}
//size_t	ME_FileServerClient::GetBufferAvailable()
//{
//	return	OUT_BUF_SIZE()-GetBufferUsed();
//}

bool	ME_FileServerClient::Init()				// Attach to a new client
{
	// Replaced with ME_Buffer:

	//if (!buf_out)		{	buf_out				=	new CAutoBuffer(BASE_BUFFER_INC); }
	//if (!buf_in_actual) {	buf_in_actual		=	new char [IN_BUFFER_SIZE()]; }
	//if (!buf_out_actual) {	buf_out_actual		=	new char [OUT_BUF_SIZE()]; }

	//BaseClass::Init();

	return true;
}


bool	ME_FileServerClient::Close()				// Detach from a client
{
	BaseClass::Close();
	return false;
}

bool	ME_FileServerClient::Open(char*	File, DWORD Access, DWORD Flags)
{
	// Don't open the same file twice!
	if ( !File || !CurrentFile || _stricmp(File,CurrentFile) != 0 )
	{
		if ( Socket != (SOCKET)INVALID_HANDLE_VALUE ) { CloseHandle((HANDLE)Socket); Socket = (SOCKET)INVALID_HANDLE_VALUE; }

		// Make sure the path exists!
		MKDir(File);

		// TODO: Does CreateFile block, and if it does, does it block enough to make it worthfile to open acsync,
		// and is it even possible to open async in the first place, like it is with network stream connections?
		if ((SOCKET)INVALID_HANDLE_VALUE == (Socket = (SOCKET)CreateFileA(File,Access,FILE_SHARE_READ,NULL,OPEN_ALWAYS,Flags | FILE_FLAG_OVERLAPPED,NULL) ))
		{
			return false;
		}

		// Clear the possible ERROR_ALREADY_EXISTS
		SetLastError(0);

		if (0 == BindIoCompletionCallback((HANDLE)Socket, MaxsiEngine::ME_ServerClient__DoneIO, 0) )
		{
			ME_UNDEFINED_BEHAVIOR("ME_FileServerClient::Open() couldn't BindIoCompletionCallback()!");
			return false;
		}

		if ( CurrentFile ) { delete[] CurrentFile; }
		CurrentFile		=	BuildString(1,File);
	}

	Opened();

	return true;
}

bool	ME_FileServerClient::Opened()
{
	SetMode(FileServer_FileOpen);

	return true;
}


bool	ME_FileServerClient::Think()
{
	// Remember to call DoThink() and not Think().

	if ( Mode() == FileServer_Inactive )
	{
		ThinkModeFileServer_Inactive();
	}	
	if ( Mode() == FileServer_ReadingCommand )
	{
		ThinkModeFileServer_ReadingCommand();
	}
	if ( Mode() == FileServer_FileOpen )
	{
		ThinkModeFileServer_FileOpen();
	}
	
	// Hack to make sure we keep thinking if we are idle and there are more commands pending.
	if ( Mode() == FileServer_Inactive && Output->GetBufferUsed() >= sizeof(m_Command)  )
	{
		return	Think(); // We are already in a think, so it's okay to do another one (no reason to call DoThink() and lock the critical section twice)
	}

	if ( Mode() == FileServer_Inactive )
	{
		// Looks like we don't have anything to do - so don't do anything!
		ScheduleNextThink(ME_NEXT_THINK_DONT);		
	}

	return	true;

}

bool	ME_FileServerClient::ThinkModeFileServer_Inactive()
{
	int	Command = 0;

	// We are not doing anything yet.

	Output->Lock(ME_BUFFER_LOCK_PARAMS);
		
	if ( Output->MayRead() == ME_ERROR_SUCCESS && Output->Read((char*)&Command,sizeof(Command)) == ME_ERROR_SUCCESS )
	{
#ifdef _DEBUG
		// Helps you pinpoint where the wrong read happened.
		char*	LocationOfProblem	=	Output->GetPointer()-sizeof(Command);
#endif
		if ( Command > 2 || Command < 0)
		{
			ME_UNDEFINED_BEHAVIOR("ME_FileServetClient::ThinkModeFileServer_Inactive() read an unknown command! The Output buffer may have been handled incorrectly.");
		}

		SetCommand(Command);
		SetMode(FileServer_ReadingCommand);
		CommandParam.Read		=	false;
	}
	else
	{
		// Looks like we don't have anything to do - so don't do anything!
		ScheduleNextThink(ME_NEXT_THINK_DONT);		
	}

	Output->Unlock();

	return	true;
}

bool	ME_FileServerClient::ThinkModeFileServer_ReadingCommand()
{
	Output->Lock(ME_Buffer_Flag_Read ME_BUFFER_LOCK_PARAMSEX);

	// We are recieving a command, see if we have recieved its parameters too
	
	if ( !CommandParam.Read )
	{
		if ( Output->MayRead() == ME_ERROR_SUCCESS && Output->Read((char*)&CommandParam,sizeof(CommandParam)) == ME_ERROR_SUCCESS )
		{
			// Yes, they're there!
			CommandParam.Read	=	true;
		}
	}

	if ( CommandParam.Read )
	{
		// We have recieving a command, check out what it is
		if ( Command() == FileServer_CommandWrite )
		{
			//size_t	StartWriting		=		(size_t)CommandParam.A;
			//size_t	BufferLength		=		(size_t)CommandParam.B;
			//size_t	FileNameLength		=		(size_t)CommandParam.C;
			//size_t	WrittenSoFar		=		(size_t)CommandParam.D;

			if (Output->GetBufferUsed() >= (size_t)CommandParam.C )
			{
				// Okay, we have recieved a filename. Excellent!
				
				// Allocate memory to store it in
				char*		FileName	=	new char[(size_t)CommandParam.C];

				// Copy the filename into our buffer. We could have simply passed a pointer to the buffer
				// but that isn't safe.
				Output->Read(FileName,(size_t)CommandParam.C);

				// Open the file. This function will be called again when the file is opened, or
				// instantly if the file is opened at once.
				// We specify FILE_FLAG_SEQUENTIAL_SCAN because we are writing cronologically.
				if (!Open(FileName,GENERIC_WRITE,FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL))
				{
					ME_UNDEFINED_BEHAVIOR("ME_FileServerClient::ThinkModeFileServer_ReadingCommand() couldn't write file!");
				}

				CommandParam.D	=	0;

				delete[]	FileName;
			}
		}
		else if ( Command() == FileServer_CommandDecompressTorrent )
		{
			//size_t	StartWriting		=		(size_t)CommandParam.A;
			//size_t	BufferLength		=		(size_t)CommandParam.B;
			//size_t	FileNameLength		=		(size_t)CommandParam.C;
			//size_t	ReadSoFar			=		(size_t)CommandParam.D;

			if (Output->GetBufferUsed() >= (size_t)CommandParam.C )
			{
				// Okay, we have recieved a filename. Excellent!
				
				// Allocate memory to store it in
				char*		FileName	=	new char[(size_t)CommandParam.C];

				// Copy the filename into our buffer. We could have simply passed a pointer to the buffer
				// but that isn't safe.
				Output->Read(FileName,(size_t)CommandParam.C);

				// Make sure any previous file handles wont cause us trouble.
				delete[]	CurrentFile;	CurrentFile		=	0;

				// Open the file. This function will be called again when the file is opened, or
				// instantly if the file is opened at once.
				// We specify FILE_FLAG_SEQUENTIAL_SCAN because we are reading cronologically.
				if (!Open(FileName,GENERIC_READ,FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL))
				{
					ME_UNDEFINED_BEHAVIOR("ME_FileServerClient::ThinkModeFileServer_ReadingCommand() couldn't read file!");
				}

				// From now on CommandParam.C specifies if the file system has decompressed the data and
				// how far it is extracting the file data.
				// If CommandParam.C == 0 then the data isn't decompresed
				// If CommandParam.C == 0 then CommandParam.D tells how much data has been read from the compressed chunk.
				// If CommandParam.C > 0 then the file system should write file CommandParam.C in this block.
				// If CommandParam.C > 0 then CommandParam.D tells how much data has been written to the chunk.

				CommandParam.C	=	0;

				delete[]	FileName;
			}
		}
		else
		{
			ME_UNDEFINED_BEHAVIOR("ME_FileServerClient::ThinkModeFileServer_ReadingCommand() read unknown command!");
		}
	}

	Output->Unlock();

	return	true;
}


bool	ME_FileServerClient::ThinkModeFileServer_FileOpen()
{
	CommandParam.Read	=	false;

	// We have opened a file and ...
	if ( Command() == FileServer_CommandWrite )
	{
		ThinkModeFileServer_FileOpen_CommandWrite();
	}
	else if ( Command() == FileServer_CommandDecompressTorrent )
	{
		ThinkModeFileServer_FileOpen_CommandDecompressTorrent();
	}
	else
	{
		ME_UNDEFINED_BEHAVIOR("ME_FileServetClient::ThinkModeFileServer_FileOpen() tried to execute an unknown command!");
	}

	return	true;
}

bool	ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandWrite()
{
	// ... we are going to write to it.

	Output->Lock(ME_BUFFER_LOCK_PARAMS);

	if ( !Output->IsInUse() && Output->MayRead() == ME_ERROR_SUCCESS && Output->MayWrite() == ME_ERROR_SUCCESS )
	{
		size_t	StartWriting		=		(size_t)CommandParam.A;
		size_t	BufferLength		=		(size_t)CommandParam.B;
		//size_t	FileNameLength		=		(size_t)CommandParam.C;
		size_t	WrittenSoFar		=		(size_t)CommandParam.D;

		//size_t	WriteFrom			=		0;
		size_t	WriteLength			=		0;

		if ( BufferLength == 0 )
		{
			// We have written all the data. Simply proceed to the next command.			
			SetMode(FileServer_Inactive);
		}
		else
		{
			WriteLength		=	BufferLength;

			char*	Pointer	=	Output->GetPointer();

			// Do we have the entire buffer in one row?			
			if ( Output->GetPointerSizeFollowingData() <= /*Output->GetBufferOffset()+*/WriteLength )
			{
				WriteLength	=	Output->GetPointerSizeFollowingData();
			}
			
			Out.Offset			=	StartWriting+WrittenSoFar;
			
			Output->SetInUse();

			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

			if ( WriteFile((HANDLE)Socket,
				Pointer,
				(DWORD)WriteLength,
				NULL,
				&Out) == FALSE )
			{
				DWORD nLastError = GetLastError();
				if (ERROR_IO_PENDING != nLastError)
				{
					char*	WroteStr	=	UIToA(nLastError);	
					char*	Error		=	BuildString(3,"Write Failure #",WroteStr," in ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandWrite() !");
					Server->Log(Error);
					delete[] Error;
					delete[] WroteStr;

					// What should we do now?
					ScheduleNextThink(ME_NEXT_THINK_NONE);					
					Output->ClearInUse();

					ME_UNDEFINED_BEHAVIOR("Write operation in ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandWrite() failed!");
					ME_DONT_COMPILE_RELEASE_BUILD();
				}
			}
			else
			{
				// We wrote.
				//Output->ProgressBuffer(WriteLength);
				// Todo: Is it correct to outcomment the above?
				// Todo: Turns out it is. Just remove this!
				ME_DONT_COMPILE_RELEASE_BUILD();
			}

		}
	}
	else
	{
		ScheduleNextThink(ME_NEXT_THINK_MECALLBACK);		
	}

	Output->Unlock();

	return	true;
}

bool	ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandDecompressTorrent()
{
	// ... we are going to uncompress it and extract to the harddrive!

	Input->Lock(ME_BUFFER_LOCK_PARAMS);

	// If this is zero, then we have not uncompressed the data yet.
	if ( CommandParam.C == 0 )
	{
		if ( !Input->IsInUse() && Input->MayRead() == ME_ERROR_SUCCESS && Input->MayWrite()  == ME_ERROR_SUCCESS)
		{
			//size_t	BlockSize			=		(size_t)CommandParam.A;
			size_t	BlockId				=		(size_t)CommandParam.B;
			size_t	ReadSoFar			=		(size_t)CommandParam.D;
			size_t	FileSize			=		(size_t)GetFileSize((HANDLE)Socket,NULL);
			MaxsiTorrentData*	TorrentData		=	(MaxsiTorrentData*)CommandParam.E;

			// Have we read the whole buffer?
			if ( ReadSoFar >= FileSize )
			{
				// We use GetFileSize and not GetFileSizeEx here because the blocks should be smaller than 2^32 bytes... ^_^
				// Decompress the data

				uLong	Decompressed_Size	=	MaxsiTorrent_BlockSize;
				
				int		Result				=	uncompress((Bytef*)Input->GetBuffer()+MaxsiTorrent_BlockSize,&Decompressed_Size,(Bytef*)Input->GetBuffer(),ReadSoFar);
			
				if ( Result == Z_OK )
				{
					char*				SHA1		=	CalculateSHA1(Input->GetBuffer()+MaxsiTorrent_BlockSize,(size_t)Decompressed_Size);
					
					if ( memcmp(SHA1,TorrentData->pBlocks[BlockId]->SHA1,40) != 0 )
					{
						char*	strBlockId	=	UIToA((unsigned int)BlockId);
						char*	Error		=	BuildString(3,"HASH-Check failed for block #",strBlockId,"!");

						SetMode(FileServer_Inactive);

						Server->Log(Error);
						TorrentData->Server->OnBlockFailed(TorrentData->pBlocks[BlockId]);
					}
					else
					{
						// We have successfully uncompressed the block, proceed to extract it to the proper places.
						if ( Socket != (SOCKET)INVALID_HANDLE_VALUE ) { CloseHandle((HANDLE)Socket); Socket = (SOCKET)INVALID_HANDLE_VALUE; }
						CommandParam.D		=	0;
						CommandParam.C		=	1;

					}
				}
				else
				{
					char*	strBlockId	=	UIToA((unsigned int)BlockId);
					char*	Error		=	BuildString(3,"Couldn't decompress Block #",strBlockId,". Invalid data might have been recieved.");
					
					SetMode(FileServer_Inactive);

					Server->Log(Error);
					TorrentData->Server->OnBlockFailed(TorrentData->pBlocks[BlockId]);
				}
			}
			else
			{
				In.Offset			=	ReadSoFar;

				if ( ReadSoFar > FileSize )
				{
					ME_UNDEFINED_BEHAVIOR("Decompression Filesystem read beyond Filesize!");
				}

				if ( FileSize > MaxsiTorrent_BlockSize )
				{
					// Todo: Should we have error handling here?
					ME_UNDEFINED_BEHAVIOR("Block was larger than allocated block memory! (FileSize > MaxsiTorrent_BlockSize)");
				}

				Input->SetInUse();
				ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

				if ( ReadFile((HANDLE)Socket,
					Input->GetBuffer()+ReadSoFar,
					(DWORD)FileSize-ReadSoFar,
					NULL,
					&In) == FALSE )
				{
					DWORD nLastError = GetLastError();
					if (ERROR_IO_PENDING != nLastError)
					{
						char*	WroteStr	=	UIToA(nLastError);	
						char*	ErrorMSG		=	BuildString(3,"Read Failure #",WroteStr," in ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandDecompressTorrent() !");
						Server->Log(ErrorMSG);
						ME_UNDEFINED_BEHAVIOR(ErrorMSG);
						delete[] ErrorMSG;
						delete[] WroteStr;
						
						ME_DONT_COMPILE_RELEASE_BUILD();

						// I don't think this works yet.

						Input->ClearInUse();
						ScheduleNextThink(ME_NEXT_THINK_NONE);
						SetMode(FileServer_Inactive);
						TorrentData->Server->OnBlockFailed(TorrentData->pBlocks[BlockId]);
					}
				}
				else
				{
					// TODO: Can we read again?
					//ReadSoFar			+=	nWritten;
					//CommandParam.D		+=	nWritten;
					//IsReading			=	FALSE;
				}
			}
		}
		else
		{
			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);
		}
	}
	// If non-zero, then we have uncompressed the data now, and we should extract the data to the proper places.
	if ( CommandParam.C )
	{
		if ( !Input->IsInUse() && Input->MayRead() == ME_ERROR_SUCCESS && Input->MayWrite()  == ME_ERROR_SUCCESS)
		{
			size_t	BlockSize			=		(size_t)CommandParam.A;
			size_t	BlockId				=		(size_t)CommandParam.B;
			size_t	CurrentFile			=		(size_t)CommandParam.C-1;
			size_t	LeftToWrite			=		(size_t)CommandParam.D;
			size_t	FileSize			=		-1;
			MaxsiTorrentData*	TorrentData		=	(MaxsiTorrentData*)CommandParam.E;
			MaxsiTorrentBlock*	pBlock			=	TorrentData->pBlocks[BlockId];
			size_t	NumFiles			=	pBlock->FileEnd-pBlock->FileBegin+1;

			if ( Socket != (SOCKET)INVALID_HANDLE_VALUE )
			{
				if ( LeftToWrite == 0 )
				{
					CommandParam.C++;
					CurrentFile++;
					CommandParam.D	=	0;
					LeftToWrite		=	0;
				}
			}

			bool	ShouldContinue	=	true;

			while ( ShouldContinue )
			{
				if ( CurrentFile < NumFiles )
				{
					// Find out what portion of the file is inside this block.
					MaxsiTorrentFile*	pFile		=	TorrentData->pFiles[pBlock->FileBegin+CurrentFile];
					size_t				FileStart	=	pFile->BlockBegin;
					size_t				FileEnd		=	pFile->BlockEnd;

					if ( FileStart > FileEnd )
					{
						ME_UNDEFINED_BEHAVIOR("Tried to extract a decompressed file, but its end was set past its start!");
					}

					size_t				BlockStart	=	BlockSize*BlockId;
					size_t				BlockEnd	=	BlockSize*(BlockId+1);

					if ( FileEnd < BlockStart )
					{
						ME_UNDEFINED_BEHAVIOR("Tried to extract a decompressed file, but FileEnd < BlockStart!");
					}
					if ( FileStart > BlockEnd )
					{
						ME_UNDEFINED_BEHAVIOR("Tried to extract a decompressed file, but FileStart > BlockEnd!");
					}
					
					if ( min(FileEnd,BlockEnd) < max(FileStart,BlockStart) )
					{
						ME_UNDEFINED_BEHAVIOR("Tried to extract a decompressed file, but min(FileEnd,BlockEnd) < max(FileStart,BlockStart)!");
					}

					size_t				Offset		=	max(FileStart,BlockStart);
					size_t				Length		=	min(FileEnd,BlockEnd)-Offset;



					char*				FileName	=	BuildString(2,TorrentData->DirectoryName,pFile->FileName);						

					bool				Result		=	Open(FileName,GENERIC_WRITE,FILE_FLAG_SEQUENTIAL_SCAN | FILE_ATTRIBUTE_NORMAL);

					if ( !Result )
					{
						char*	ErrorCode	=	UIToA((unsigned int)GetLastError());
						char*	ErrorMSG	=	BuildString(4,"ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandDecompressTorrent() Couldn't open ",FileName," for write! Error Code is: ",ErrorCode);
						ME_UNDEFINED_BEHAVIOR(ErrorMSG);
						ShouldContinue	=	false;
						delete[] ErrorMSG;
						delete[] ErrorCode;
					}
					else
					{
						// Set the file size of the file.

						FileSize	=	FileEnd-FileStart;

						SetFilePointer((HANDLE)Socket,FileSize,NULL,FILE_BEGIN);
						SetEndOfFile((HANDLE)Socket);

						FileSize			=		Length;

						if ( LeftToWrite == 0 )
						{
							LeftToWrite		=	FileSize;
							CommandParam.D	=	FileSize;
						}

						// This ME_UNDEFINED_BEHAVIOR seems to prevent what could be normal behavior,
						// so I outcommented it. If you can prove that it is unnessary or if it
						// prevents behavior that is acceptable, then please remove it fully.
						ME_DONT_COMPILE_RELEASE_BUILD();
						//// Amount of data available < Amount of data written
						//if ( Length < FileSize-LeftToWrite )
						//{
						//	ME_UNDEFINED_BEHAVIOR("ThinkModeFileServer_FileOpen_CommandDecompressTorrent had Length < FileSize-LeftToWrite!");
						//}

						// Write data here!
						In.Offset			=	Offset-FileStart+(FileSize-LeftToWrite);
						
						char*	dP			=	Input->GetBuffer()+MaxsiTorrent_BlockSize+Offset-BlockStart+(FileSize-LeftToWrite);
						DWORD	dL			=	(DWORD)Length-(FileSize-LeftToWrite);

						if ( dL == 0)
						{
							// This is a zero-length file. No need to write data, we have already made sure it is empty. Simply loop this code for the next file.
							ShouldContinue	=	true;
							CurrentFile++;
							CommandParam.C++;
						}
						else
						{
							// There is data to be written, simply write it.

							ShouldContinue		=	false;
							
							Input->SetInUse();
							ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);

							if ( WriteFile((HANDLE)Socket,
								dP,
								dL,
								NULL,
								&In) == FALSE )
							{
								DWORD nLastError = GetLastError();
								if (ERROR_IO_PENDING != nLastError)
								{
									char*	WroteStr	=	UIToA(nLastError);	
									char*	ErrorMSG		=	BuildString(3,"Write Failure #",WroteStr," in ME_FileServerClient::ThinkModeFileServer_FileOpen_CommandDecompressTorrent() !");
									Server->Log(ErrorMSG);
									ME_UNDEFINED_BEHAVIOR(ErrorMSG);
									delete[] ErrorMSG;
									delete[] WroteStr;

									ME_DONT_COMPILE_RELEASE_BUILD();

									// Is the following correct?
									
									
								}
							}
						}
					}
				}
				else
				{
					// Local scope
					{
					char*	BlockId			=	UIToA(CommandParam.B);
					char*	FileName		=	BuildString(5,TorrentData->DirectoryName,TorrentData->TorrentName,"_Block_",BlockId,".MaxsiDistribution" );

					remove(FileName);

					delete[]	FileName;
					delete[]	BlockId;
					}

					SetMode(FileServer_Inactive);

					TorrentData->pBlocks[BlockId]->Flags	|=	Block_Written;

					TorrentData->Server->EvaluateServer();

					ShouldContinue			=	false;
				}
			}
		}
		else
		{
			ScheduleNextThink(ME_NEXT_THINK_WINCALLBACK);
		}
	}

	Input->Unlock();

#ifdef _DEBUG
	if ( NextThink == ME_NEXT_THINK_NONE && Mode() != FileServer_Inactive )
	{
		// Catch these early on.
		__debugbreak();
	}
#endif


	return	true;
}


void CALLBACK ME_FileServerClient::DoneIO(DWORD dwErrorCode,
					 DWORD dwNumberOfBytesTransferred,
					 LPOVERLAPPED lpOverlapped)
{

	DWORD	Error	=	ERROR_SUCCESS;

	if ( dwErrorCode )
	{
		if ( GetOverlappedResult((HANDLE)Socket,lpOverlapped,&dwNumberOfBytesTransferred,FALSE) == 0)
		{
			Error		=	GetLastError();
			if ( Error	==	ERROR_IO_INCOMPLETE )
			{
				Error	=	ERROR_SUCCESS;
			}
		}
	}

	if ( Error )
	{
		char*	ErrorStr	=	UIToA((unsigned int)Error);
		char*	FullError	=	BuildString(3,"Windows System Error Code ",ErrorStr," occured in ME_FileServerClient::DoneIO!");
		ME_UNDEFINED_BEHAVIOR(FullError);
		delete[] FullError;
		delete[] ErrorStr;
	}

	OVERLAPPED_PLUS*	ov		=	(OVERLAPPED_PLUS *)lpOverlapped;	

	if (0 != dwNumberOfBytesTransferred)
	{
		// Let's rock
		// First, see which type of operation completed: read or write.
		if (ov->BufferType == ME_BUFFER_TYPE_WRITE)
		{
			Output->Lock(ME_BUFFER_LOCK_PARAMS);

			// The data that was in use has now been written and should no longer be marked
			// as in use thus giving the code permission to use the buffer again.
			Output->ClearInUse();

			EnterCriticalSection(&MayExecThinkFunction);

			if ( Command() == FileServer_CommandWrite )
			{
				Output->ProgressBuffer(dwNumberOfBytesTransferred);

				CommandParam.B		-=	dwNumberOfBytesTransferred;
				CommandParam.D		+=	dwNumberOfBytesTransferred;
			}
			else if ( Command() == FileServer_CommandDecompressTorrent )
			{
				// TODO: THIS SHOULD NOT BE CALLED ANYMORE - PLEASE REMOVE
				// WHEN ENSURED THAT THIS ISN'T CALLED.
				ME_DONT_COMPILE_RELEASE_BUILD();
				__debugbreak();
				CommandParam.D		-=	dwNumberOfBytesTransferred;
			}
			
			LeaveCriticalSection(&MayExecThinkFunction);

			// Todo: Old debug stuff. Please remove when MD ships.
			ME_DONT_COMPILE_RELEASE_BUILD();
			size_t	MemAvailable	=	Output->GetBufferAvailable();

			Output->Unlock();

			// Wrote data. Thus we have more space for more requests.

			ME_FileServer*	FileServer	=	dynamic_cast<ME_FileServer*>(Server);

			if (FileServer && FileServer->OnBufferExpanded)
			{
				
				if ( BufferOverflowDetected )
				{
					BufferOverflowDetected	=	false;
				}
				if ( MemAvailable < 2048 )
				{
					ME_DONT_COMPILE_RELEASE_BUILD();
					// We have a very low memory situation that probably can't handle another request, even
					// though calling this function means we have enough space. It appears that the torrent client
					// somehow fails to call us back if this is the case, or something leading to an internal deadlock
					// or just stops all thinking.
					//__debugbreak();
				}
				FileServer->OnBufferExpanded(dynamic_cast<ME_FileServer*>(Server));
			}

			
		}
		else
		{
			// Read data.

			// Todo: We don't use the ME_Buffer's own usage system.

			Input->Lock(ME_BUFFER_LOCK_PARAMS);

			Input->ClearInUse();
			
			Input->Unlock();

			EnterCriticalSection(&MayExecThinkFunction);

			if ( Command() == FileServer_CommandDecompressTorrent && CommandParam.C == 0)
			{
				CommandParam.D		+=	dwNumberOfBytesTransferred;
			}
			else if ( Command() == FileServer_CommandDecompressTorrent && CommandParam.C )
			{
				CommandParam.D		-=	dwNumberOfBytesTransferred;
			}
			
			LeaveCriticalSection(&MayExecThinkFunction);
		}
	}

	DoThink();
}

bool ME_FileServerClient::MaintainConnection()
{
	// Todo: We don't really use this. Please expand this system.
	TimeStamp();
	return true;
}

// Replaced by ME_Buffer

//bool	ME_FileServerClient::GetBufferData(void* dest, size_t Length)
//{
//	bool	Result	=	true;
//
//	Enter();
//
//	if ( BufferUsed < Length )
//	{
//		Result		=	false;		
//	}
//	else
//	{
//		// In this buffer, the byte after the last byte should be the first byte, so
//		// if we cross the boundary, we need to do two operations.
//		if ( Length + GetBufferOffset() > OUT_BUF_SIZE() )
//		{
//			//memcpy((char*)dest,buf_out_actual+GetBufferOffset(),OUT_BUF_SIZE()-GetBufferOffset());
//			//memcpy((char*)dest+OUT_BUF_SIZE()-GetBufferOffset(),buf_out_actual,Length-(OUT_BUF_SIZE()-GetBufferOffset()));
//			size_t	cGetBufferOffset	=	GetBufferOffset();
//			size_t	cOUT_BUF_SIZE		=	OUT_BUF_SIZE();
//			memcpy((char*)dest,buf_out_actual+cGetBufferOffset,cOUT_BUF_SIZE-cGetBufferOffset);
//			memcpy((char*)dest+cOUT_BUF_SIZE-cGetBufferOffset,buf_out_actual,Length-(cOUT_BUF_SIZE-cGetBufferOffset));
//		}
//		else
//		{
//			memcpy((char*)dest,buf_out_actual+GetBufferOffset(),Length);
//		}
//
//		// Mark this memory as read and now unused.
//		BufferOffset	+=	Length;
//		BufferUsed		-=	Length;
//		BufferOffset	%=	OUT_BUF_SIZE();
//	}
//
//	Leave();
//
//	return Result;
//}
//
//// Make sure we are calling this from within a Critical Section!
//bool	ME_FileServerClient::SetBufferData(void* src, size_t Length)
//{
//	bool	Result	=	true;
//
//	Enter();
//
//	if ( Length > GetBufferAvailable() )
//	{
//		Result		=	false;		
//	}
//	else
//	{
//		// In this buffer, the byte after the last byte should be the first byte, so
//		// if we cross the boundary, we need to do two operations.
//		
//		size_t	BufferEnd	=	(GetBufferOffset() + GetBufferUsed() - CurrentlyWriting) % OUT_BUF_SIZE();		
//
//		if ( BufferEnd + Length > OUT_BUF_SIZE() )
//		{
//			memcpy(buf_out_actual+BufferEnd,(char*)src,OUT_BUF_SIZE()-BufferEnd);
//			memcpy(buf_out_actual,(char*)src+OUT_BUF_SIZE()-BufferEnd,Length-OUT_BUF_SIZE()+BufferEnd);
//		}
//		else
//		{
//			memcpy(buf_out_actual+BufferEnd,(char*)src,Length);
//		}
//
//		// Mark this memory as used
//		BufferUsed		+=	Length;
//	}
//
//	Leave();
//
//	return true;
//}


bool	ME_FileServerClient::AsyncWriteTorrentBlock(
	MaxsiTorrentData* TorrentData,
	MaxsiTorrentBlock* Block,
	ME_TorrentServerClient* Client,
	char* Buffer,
	size_t Offset,
	size_t BlockSize,
	size_t BufferSize,
	bool Compressed)
{
	// Todo: Outdated debug stuff. Please remove.
	ME_DONT_COMPILE_RELEASE_BUILD();
	//if (	(int)Client->buf_in_actual+Client->IN_BUFFER_SIZE() < (int)Buffer ||
	//		(int)Client->buf_in_actual > (int)Buffer ||
	//		(int)Client->buf_in_actual+Client->IN_BUFFER_SIZE() < (int)Buffer+BufferSize ||
	//		(int)Client->buf_in_actual > (int)Buffer+BufferSize )
	//{
	//	ME_DONT_COMPILE_RELEASE_BUILD();
	//	ME_UNDEFINED_BEHAVIOR("Tried to write more data to a file than was in the buffer!");
	//}

	bool	Result		=	false;

	Output->Lock(ME_BUFFER_LOCK_PARAMS);
	
	if (!Compressed)
	{
		// TODO: MAKE SUPPORT FOR NON-COMPRESSED DOWNLOADS!
		Result			=	false;
		ME_TODO("Non-Compressed Downloads isn't supported!");
	}
	else
	{
		// Store stuff in a .MaxsiDistribution file
		char*	BlockId			=	UIToA(Block->Id);
		char*	FileName		=	BuildString(5,TorrentData->DirectoryName,TorrentData->TorrentName,"_Block_",BlockId,".MaxsiDistribution" );

		int							NewCommand		=	FileServer_CommandWrite;
		ME_FileServerClientCommand	NewCommandParam;
		NewCommandParam.Read	=	true;
		NewCommandParam.A		=	Offset;
		NewCommandParam.B		=	BufferSize;
		NewCommandParam.C		=	strlen(FileName)+1;
		NewCommandParam.D		=	0;

		size_t	RequestSize		=	sizeof(NewCommand)+sizeof(NewCommandParam)+NewCommandParam.C+BufferSize;

		// We require RequestSize in the out buffer. Make sure we have that size, or fail.

		size_t	Available	=	Output->GetBufferAvailable();

		if ( RequestSize > Available )
		{
			// Dang. Out of buffer space!
			// Todo: Add code to debug this situation and output it to the error list, somehow.

			// Todo: This actually isn't an error, so don't output it the error list, though,
			// perhaps output it to a debug list.

			// For now, just return false
			Result							=	false;
			ME_DONT_COMPILE_RELEASE_BUILD(); // Todo: Remove silly debug stuff
			BufferOverflowDetected			=	true;
			ShouldDoSillyDebugStuffDeleteMe	=	true;
		}
		else
		{
			// Nice! We got enough buffer space!

			Output->Write((char*)&NewCommand,sizeof(NewCommand));
			Output->Write((char*)&NewCommandParam,sizeof(NewCommandParam));
			Output->Write((char*)FileName,NewCommandParam.C);
			Output->Write((char*)Buffer,BufferSize);

			// The above functions shouldn't fail, because we already made sure we got enough space.

			Result							=	true;
			BufferOverflowDetected			=	false;
		}

		delete[] FileName;
		delete[] BlockId;
	}

	Output->Unlock();

	if ( Result ) {	DoThink(); }


	ME_DONT_COMPILE_RELEASE_BUILD();
	if ( !Result ) { DoThink(); }

	return Result;
}


bool	ME_FileServerClient::FullTorrentBlockRecieved(
	MaxsiTorrentData* TorrentData,
	MaxsiTorrentBlock* Block,
	ME_TorrentServerClient* Client,
	bool Compressed)
{
	bool	Result		=	false;

	Output->Lock(ME_BUFFER_LOCK_PARAMS);
	
	if (!Compressed)
	{
		// TODO: MAKE SUPPORT FOR NON-COMPRESSED DOWNLOADS!
		Result			=	false;
		ME_TODO("Non-Compressed Downloads isn't supported!");
	}
	else
	{
		// Store stuff in a .MaxsiDistribution file
		char*	BlockId			=	UIToA(Block->Id);
		char*	FileName		=	BuildString(6,TorrentData->DirectoryName,"\\",TorrentData->TorrentName,"_Block_",BlockId,".MaxsiDistribution" );

		int							NewCommand		=	FileServer_CommandDecompressTorrent;
		ME_FileServerClientCommand	NewCommandParam;
		NewCommandParam.Read	=	true;
		NewCommandParam.A		=	MaxsiTorrent_BlockSize;
		NewCommandParam.B		=	Block->Id;
		NewCommandParam.C		=	strlen(FileName)+1;
		NewCommandParam.D		=	0;
		NewCommandParam.E		=	(int)TorrentData;


		size_t	RequestSize		=	sizeof(NewCommand)+sizeof(NewCommandParam)+NewCommandParam.C;

		// We require RequestSize in the out buffer. Make sure we have that size, or fail.

		size_t	Available	=	Output->GetBufferAvailable();

		if ( RequestSize > Available )
		{
			// Dang. Out of buffer space!
			// Todo: Add code to debug this situation and output it to the error list, somehow.

			// For now, just return false
			Result							=	false;
			BufferOverflowDetected			=	true;
			ShouldDoSillyDebugStuffDeleteMe	=	true;
		}
		else
		{
			// Nice! We got enough buffer space!

			Output->Write((char*)&NewCommand,sizeof(NewCommand));
			Output->Write((char*)&NewCommandParam,sizeof(NewCommandParam));
			Output->Write((char*)FileName,NewCommandParam.C);

			// The above functions shouldn't fail, because we already made sure we got enough space.

			Result							=	true;
			BufferOverflowDetected			=	false;
		}

		delete[] FileName;
		delete[] BlockId;
	}

	Output->Unlock();

	if ( Result ) {	DoThink(); }

	ME_DONT_COMPILE_RELEASE_BUILD();
	if ( !Result ) { DoThink(); }
	

	return Result;
}


//=============================================================================
//	class ME_FileServer
//=============================================================================

ME_FileServer::ME_FileServer()
{
	OnBufferExpanded	=	0;	
	ShouldMainLoop		=	false;
	ShouldListen		=	false;
	MaxConnections		=	1;
}

ME_FileServer::~ME_FileServer()
{
}

bool	ME_FileServer::HandleRequest	(ME_ServerClient* Client, char* Parameter)
{
	return true;
}


ME_ServerClient*	ME_FileServer::CreateIOCPClient( void )
{
	return (ME_ServerClient*) new ME_FileServerClient;
}


bool	ME_FileServer::RunServer(short Port)
{
	return	BaseClass::RunServer(Port);
}
bool	ME_FileServer::RunServerEx(short Port, size_t NumConnections, size_t CheckTime)
{
	return	BaseClass::RunServerEx(Port,NumConnections,CheckTime);
}


bool	ME_FileServer::OnClientCreated	(ME_ServerClient* Client)
{
	ME_FileServerClient*		Peer		=	dynamic_cast<ME_FileServerClient*>(Client);
	if ( !Peer ) { return false; }

	Peer->Init();

	return true;
}

bool	ME_FileServer::OnClientConnected	(SOCKET s, int Error)
{
	return	BaseClass::OnClientConnected(s,Error);
}

bool	ME_FileServer::AsyncWriteTorrentBlock(
	MaxsiTorrentData* TorrentData,
	MaxsiTorrentBlock* Block,
	ME_TorrentServerClient* Client,
	char* Buffer,
	size_t Offset,
	size_t BlockSize,
	size_t BufferSize,
	bool Compressed)
{
	return dynamic_cast<ME_FileServerClient*>(Clients[0])->AsyncWriteTorrentBlock(TorrentData,Block,Client,Buffer,Offset,BlockSize,BufferSize,Compressed);
}

bool	ME_FileServer::FullTorrentBlockRecieved(
	MaxsiTorrentData* TorrentData,
	MaxsiTorrentBlock* Block,
	ME_TorrentServerClient* Client,
	bool Compressed)
{
	return dynamic_cast<ME_FileServerClient*>(Clients[0])->FullTorrentBlockRecieved(TorrentData,Block,Client,Compressed);
}